#include <sdk.h>
#include "app.h"
#include <wx/arrstr.h>
#include <wx/fs_zip.h>
#include <wx/fs_mem.h>
#include <wx/xrc/xmlres.h>
#include <wx/cmdline.h>
#include <wx/regex.h>
#include <wx/filefn.h>
#include <wx/log.h>
#include <wx/msgdlg.h>
#include <wx/choicdlg.h>
#include <wx/notebook.h>
#include <wx/clipbrd.h>
#include <wx/debugrpt.h>
#include <wx/ipc.h>
#include <wx/msgout.h>
#include <M_Code_exception.h>
#include <configmanager.h>
#include <editormanager.h>
#include <globals.h>
#include <loggers.h>
#include <logmanager.h>
#include <manager.h>
#include <personalitymanager.h>
#include <pluginmanager.h>
#include <projectmanager.h>
#include <scriptingmanager.h>
#include <sdk_events.h>
#include <sqplus.h>
#include "M_Code_auibook.h"
#include "M_Code_StyledTextCtrl.h"
#include "projectmanagerui.h"
#include "splashscreen.h"
#include "prefix.h"
#define APP_PREFIX ""

inline wxString GetResourcesDir() {
  return wxEmptyString;
}

namespace {
  bool s_Loading = false;

  class DDEServer : public wxServer {
    public:
      DDEServer( MainFrame* frame ) : m_Frame( frame ) { ; }
      wxConnectionBase *OnAcceptConnection( const wxString& topic );
      MainFrame* GetFrame() {
        return m_Frame;
      }
      void SetFrame( MainFrame* frame ) {
        m_Frame = frame;
      }
    private:
      MainFrame* m_Frame;
  };

  class DDEConnection : public wxConnection {
    public:
      DDEConnection( MainFrame* frame ) : m_Frame( frame ) { ; }
      bool OnExecute( const wxString& topic, wxChar *data, int size, wxIPCFormat format );
      bool OnDisconnect();
    private:
      MainFrame* m_Frame;
  };

  wxConnectionBase* DDEServer::OnAcceptConnection( const wxString& topic ) {
    return topic == DDE_TOPIC ? new DDEConnection( m_Frame ) : nullptr;
  }

  bool DDEConnection::OnExecute( M_Code_unused const wxString& topic, wxChar *data, M_Code_unused int size, M_Code_unused wxIPCFormat format ) {
    wxString strData( ( wxChar* )data );
    if( strData.StartsWith( _T( "[IfExec_Open(\"" ) ) ) {
      return false;
    }
    if( strData.StartsWith( _T( "[Open(\"" ) ) ) {
      wxRegEx reCmd( _T( "\"(.*)\"" ) );
      if( reCmd.Matches( strData ) ) {
        const wxString file = reCmd.GetMatch( strData, 1 );
        M_Code_App* cb = ( M_Code_App* )wxTheApp;
        if( cb ) {
          cb->AddFileToOpenDelayed( file );
        }
      }
      return true;
    } else if( strData.StartsWith( _T( "[OpenLine(\"" ) ) ) {
      wxRegEx reCmd( _T( "\"(.*)\"" ) );
      if( reCmd.Matches( strData ) ) {
        wxString file = reCmd.GetMatch( strData, 1 );
        M_Code_App* cb = ( M_Code_App* )wxTheApp;
        cb->SetAutoFile( file );
      }
      return true;
    } else if( strData.StartsWith( _T( "[Raise]" ) ) ) {
      if( m_Frame ) {
        if( m_Frame->IsIconized() ) {
          m_Frame->Iconize( false );
        }
        m_Frame->Raise();
      }
      return true;
    } else if( strData.StartsWith( _T( "[CmdLine({" ) ) ) {
      int pos = strData.Find( _T( "})]" ) );
      if( pos != wxNOT_FOUND ) {
        wxString line = strData.Mid( 10, pos - 10 );
        line.Replace( _T( "\\)" ), _T( ")" ) );
        line.Replace( _T( "\\(" ), _T( "(" ) );
        M_Code_App* cb = ( M_Code_App* )wxTheApp;
        if( m_Frame && !line.empty() ) {
          cb->ParseCmdLine( m_Frame, line );
          CodeBlocksEvent event( cbEVT_APP_CMDLINE );
          Manager::Get()->ProcessEvent( event );
        }
      }
      return true;
    }
    wxSafeShowMessage( _T( "警告" ), F( _T( "DDE topic %s not handled." ), strData.wx_str() ) );
    return false;
  }

  bool DDEConnection::OnDisconnect() {
    if( !s_Loading && m_Frame ) {
      M_Code_App* cb = ( M_Code_App* )wxTheApp;
      cb->LoadDelayedFiles( m_Frame );
    }
    return true;
  }

  DDEServer* g_DDEServer = nullptr;

  class DDEClient: public wxClient {
    public:
      DDEClient( void ) {}
      wxConnectionBase *OnMakeConnection( void ) {
        return new DDEConnection( nullptr );
      }
  };
  const wxCmdLineEntryDesc cmdLineDesc[] = { {
      wxCMD_LINE_SWITCH, _T( "h" ), _T( "help" ), _T( "显示命令帮助消息" ),
      wxCMD_LINE_VAL_NONE, wxCMD_LINE_OPTION_HELP
    }, {
      wxCMD_LINE_SWITCH, _T( "s" ),  _T( "safe-mode" ), _T( "安全模式 (所有插件将被禁用)" ),
      wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL
    }, {
      wxCMD_LINE_SWITCH, _T( "ni" ), _T( "no-ipc" ), _T( "禁用数据同步" ),
      wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL
    }, {
      wxCMD_LINE_SWITCH, _T( "ns" ), _T( "no-splash-screen" ), _T( "don't display a splash screen while loading" ),
      wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL
    }, {
      wxCMD_LINE_SWITCH, _T( "mi" ),  _T( "multiple-instance" ), _T( "多实例化" ),  wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL
    }, {
      wxCMD_LINE_SWITCH, _T( "d" ), _T( "debug-log" ), _T( "显示应用程序的调试日志" ), wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL
    }, {
      wxCMD_LINE_SWITCH, _T( "nc" ), _T( "no-crash-handler" ), _T( "禁用异常处理程序" ), wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL
    }, {
      wxCMD_LINE_SWITCH, _T( "v" ), _T( "verbose" ), _T( "显示更多调试消息" ), wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL
    }, {
      wxCMD_LINE_OPTION, _T( "" ),  _T( "prefix" ), _T( "共享数据目录前缀" ), wxCMD_LINE_VAL_STRING, wxCMD_LINE_NEEDS_SEPARATOR
    }, {
      wxCMD_LINE_OPTION, _T( "" ),  _T( "user-data-dir" ), _T( "自定义用户设置和插件设置路径" ), wxCMD_LINE_VAL_STRING, wxCMD_LINE_NEEDS_SEPARATOR
    }, {
      wxCMD_LINE_OPTION, _T( "p" ), _T( "personality" ), _T( "设置配置文件 ask打开配置选择" ), wxCMD_LINE_VAL_STRING, wxCMD_LINE_NEEDS_SEPARATOR
    }, {
      wxCMD_LINE_SWITCH, _T( "" ),  _T( "no-log" ), _T( "禁用日志" ), wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL
    }, {
      wxCMD_LINE_SWITCH, _T( "" ),  _T( "log-to-file" ), _T( "保存日志文件" ), wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL
    }, {
      wxCMD_LINE_SWITCH, _T( "" ),  _T( "debug-log-to-file" ), _T( "redirect application debug log to a file" ), wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL
    }, {
      wxCMD_LINE_OPTION, _T( "" ),  _T( "profile" ), _T( "设置资源目录" ), wxCMD_LINE_VAL_STRING, wxCMD_LINE_NEEDS_SEPARATOR
    }, {
      wxCMD_LINE_SWITCH, _T( "" ),  _T( "rebuild" ), _T( "清理后构建 项目/工作空间" ), wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL
    }, {
      wxCMD_LINE_SWITCH, _T( "" ),  _T( "build" ), _T( "构建 项目/工作空间" ), wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL
    }, {
      wxCMD_LINE_SWITCH, _T( "" ),  _T( "clean" ), _T( "清理 项目/工作空间" ), wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL
    }, {
      wxCMD_LINE_OPTION, _T( "" ), _T( "target" ), _T( "批处理的目标" ), wxCMD_LINE_VAL_STRING, wxCMD_LINE_NEEDS_SEPARATOR
    }, {
      wxCMD_LINE_SWITCH, _T( "" ),  _T( "no-batch-window-close" ), _T( "在批量构建完成时不要自动关闭日志窗口" ), wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL
    }, {
      wxCMD_LINE_SWITCH, _T( "" ),  _T( "batch-build-notify" ), _T( "在批量构建完成时显示消息" ), wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL
    }, {
      wxCMD_LINE_OPTION, _T( "" ),  _T( "script" ), _T( "执行脚本文件" ), wxCMD_LINE_VAL_STRING, wxCMD_LINE_NEEDS_SEPARATOR
    }, {
      wxCMD_LINE_OPTION, _T( "" ),  _T( "file" ), _T( "打开文件并跳转到特定行 (file[:line])" ), wxCMD_LINE_VAL_STRING, wxCMD_LINE_NEEDS_SEPARATOR
    }, {
      wxCMD_LINE_PARAM, _T( "" ),  _T( "" ), _T( "打开文件" ), wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL | wxCMD_LINE_PARAM_MULTIPLE
    }, { wxCMD_LINE_NONE }
  };

  class cbMessageOutputNull : public wxMessageOutput {
    public:
      virtual void Printf( const wxChar* format, ... )  ATTRIBUTE_PRINTF_2;
  };
  void cbMessageOutputNull::Printf( M_Code_unused const wxChar* format, ... ) {}
}

IMPLEMENT_APP( M_Code_App )

BEGIN_EVENT_TABLE( M_Code_App, wxApp )
  EVT_ACTIVATE_APP( M_Code_App::OnAppActivate )
  EVT_TASKBAR_LEFT_DOWN( M_Code_App::OnTBIconLeftDown )
END_EVENT_TABLE()

bool M_Code_App::LoadConfig() {
  if( m_UserDataDir != wxEmptyString ) {
    if( !ConfigManager::SetUserDataFolder( m_UserDataDir ) ) {
      return false;
    }
  }
  ConfigManager *cfg = Manager::Get()->GetConfigManager( _T( "app" ) );
  wxString data( _T( "" ) );
  if( data.IsEmpty() ) {
    data.assign( GetAppPath() );
    data.Replace( _T( "/bin" ), _T( "" ) );
  }
  if( !m_Prefix.IsEmpty() ) {
    data = m_Prefix;
  } else {
    wxString env;
    wxGetEnv( _T( "CODE_M_DATA_DIR" ), &env );
    if( !env.IsEmpty() ) {
      data = env;
    }
  }
  data.append( _T( "/share/m" ) );
  cfg->Write( _T( "data_path" ), data );
  return true;
}

void M_Code_App::InitDebugConsole() {
}

void M_Code_App::InitExceptionHandler() {
}

MainFrame* M_Code_App::InitFrame() {
  MainFrame *frame = new MainFrame();
  wxUpdateUIEvent::SetUpdateInterval( 100 );
  SetTopWindow( nullptr );
  if( g_DDEServer && m_DDE ) {
    g_DDEServer->SetFrame( frame );
  }
  return frame;
}

bool M_Code_App::OnInit() {
  wxLog::EnableLogging( true );
  SetAppName( _T( "m_code" ) );
  s_Loading = true;
  m_pBatchBuildDialog = nullptr;
  m_BatchExitCode = 0;
  m_Batch = false;
  m_BatchNotify = false;
  m_Build = false;
  m_ReBuild = false;
  m_Clean = false;
  m_HasProject = false;
  m_HasWorkSpace = false;
  m_SafeMode = false;
  m_BatchWindowAutoClose = true;
  wxTheClipboard->Flush();
  wxCmdLineParser& parser = *Manager::GetCmdLineParser();
  parser.SetDesc( cmdLineDesc );
  wxFileSystem::AddHandler( new wxZipFSHandler );
  wxFileSystem::AddHandler( new wxMemoryFSHandler );
  wxXmlResource::Get()->InsertHandler( new wxToolBarAddOnXmlHandler );
  wxXmlResource::Get()->InsertHandler( new wxScrollingDialogXmlHandler );
  wxInitAllImageHandlers();
  wxXmlResource::Get()->InitAllHandlers();
  try {
    wxHandleFatalExceptions( true );
    InitExceptionHandler();
    delete wxMessageOutput::Set( new cbMessageOutputNull );
    if( ParseCmdLine( nullptr ) == -1 ) {
      delete wxMessageOutput::Set( new wxMessageOutputMessageBox );
      parser.Usage();
      return false;
    }
    if( !LoadConfig() ) {
      return false;
    }
    PluginManager::SetSafeMode( m_SafeMode );
    if( m_DDE && !m_Batch ) {
      DDEClient *client = new DDEClient;
      DDEConnection* connection = nullptr;
      wxLogNull ln;
      connection = ( DDEConnection * )client->MakeConnection( _T( "localhost" ), F( DDE_SERVICE, wxGetUserId().wx_str() ), DDE_TOPIC );
      if( connection ) {
        wxString cmdLine;
        for( int i = 1 ; i < argc; ++i ) {
          cmdLine += wxString( argv[i] ) + _T( ' ' );
        }
        if( !cmdLine.IsEmpty() ) {
          cmdLine.Replace( _T( "(" ), _T( "\\(" ) );
          cmdLine.Replace( _T( ")" ), _T( "\\)" ) );
          connection->Execute( _T( "[CmdLine({" ) + cmdLine + _T( "})]" ) );
        }
        connection->Execute( _T( "[Raise]" ) );
        connection->Disconnect();
        delete connection;
        delete client;
        return false;
      }
      delete client;
    }
    m_pSingleInstance = nullptr;
    if( !parser.Found( _T( "multiple-instance" ) ) ) { //  单实例
      m_DDE = true;
      const wxString name = F( _T( "m-%s" ), wxGetUserId().wx_str() );
      m_pSingleInstance = new wxSingleInstanceChecker( name, ConfigManager::GetTempFolder() );
      if( m_pSingleInstance->IsAnotherRunning() ) {
        return false;
      }
    }
    if( m_DDE && !m_Batch ) {
      g_DDEServer = new DDEServer( nullptr );
      g_DDEServer->Create( F( DDE_SERVICE, wxGetUserId().wx_str() ) );
    }
    InitDebugConsole();
    Manager::SetBatchBuild( m_Batch || !m_Script.IsEmpty() );
    Manager::Get()->GetScriptingManager();
    MainFrame* frame = nullptr;
    frame = InitFrame();
    m_Frame = frame;
    delete wxMessageOutput::Set( new wxMessageOutputBest );
    if( ParseCmdLine( m_Frame ) == 0 ) {
      if( Manager::Get()->GetConfigManager( _T( "app" ) )->ReadBool( _T( "/environment/blank_workspace" ), true ) == false ) {
        Manager::Get()->GetProjectManager()->LoadWorkspace();
      }
    }
    if( m_SafeMode ) {
      wxLog::EnableLogging( true );
    }
    if( m_Batch ) {
      Manager::SetAppStartedUp( true );
      CodeBlocksEvent event( cbEVT_APP_STARTUP_DONE );
      Manager::Get()->ProcessEvent( event );
      Manager::Get()->RegisterEventSink( cbEVT_COMPILER_FINISHED, new cbEventFunctor<M_Code_App, CodeBlocksEvent>( this, &M_Code_App::OnBatchBuildDone ) );
      s_Loading = false;
      LoadDelayedFiles( frame );
      BatchJob();
      frame->Close();
      return true;
    }
    if( !m_Script.IsEmpty() ) {
      s_Loading = false;
      LoaderBase* loader = Manager::Get()->GetFileManager()->Load( m_Script );
      if( loader->GetData() ) {
        Manager::Get()->GetScriptingManager()->LoadBuffer( cbC2U( loader->GetData() ) );
      }
      delete loader;
      frame->Close();
      return true;
    }
    try {
      wxString startup = ConfigManager::LocateDataFile( _T( "INIT.script" ), sdScriptsUser | sdScriptsGlobal );
      if( !startup.IsEmpty() ) {
        Manager::Get()->GetScriptingManager()->LoadScript( startup );
      }
    } catch( SquirrelError& exception ) {
      Manager::Get()->GetScriptingManager()->DisplayErrors( &exception );
    }
    Manager::ProcessPendingEvents();
    SetTopWindow( frame );
    frame->Show();
    frame->StartupDone();
    frame->ShowTips();
    s_Loading = false;
    LoadDelayedFiles( frame );
    Manager::Get()->GetProjectManager()->WorkspaceChanged();
    Manager::SetAppStartedUp( true );
    CodeBlocksEvent event( cbEVT_APP_STARTUP_DONE );
    Manager::Get()->ProcessEvent( event );
    return true;
  } catch( M_Code_Exception& exception ) {
    exception.ShowErrorMessage();
  } catch( SquirrelError& exception ) {
    Manager::Get()->GetScriptingManager()->DisplayErrors( &exception );
  } catch( const char* message ) {
    wxSafeShowMessage( _T( "异常" ), cbC2U( message ) );
  } catch( ... ) {
    wxSafeShowMessage( _T( "异常" ), _T( "未知异常引发应用程序终止..." ) );
  }
  return false;
}

int M_Code_App::OnExit() {
  wxTheClipboard->Flush();
  if( g_DDEServer ) {
    delete g_DDEServer;
  }
  if( m_pSingleInstance ) {
    delete m_pSingleInstance;
  }
  Manager::Free();
  return m_Batch ? m_BatchExitCode : 0;
}

inline void EnableLFH() {}

int M_Code_App::OnRun() {
  EnableLFH();
  try {
    int retval = wxApp::OnRun();
    return m_Batch ? m_BatchExitCode : retval;
  } catch( M_Code_Exception& exception ) {
    exception.ShowErrorMessage();
  } catch( SquirrelError& exception ) {
    Manager::Get()->GetScriptingManager()->DisplayErrors( &exception );
  } catch( const char* message ) {
    wxSafeShowMessage( _T( "异常" ), cbC2U( message ) );
  } catch( ... ) {
    wxSafeShowMessage( _T( "异常" ), _T( "未知异常引发应用程序终止..." ) );
  }
  return -1;
}

bool M_Code_App::OnCmdLineParsed( wxCmdLineParser& parser ) {
  return wxApp::OnCmdLineParsed( parser );
}

void M_Code_App::OnFatalException() {
  wxDebugReport report;
  wxDebugReportPreviewStd preview;
  report.AddAll();
  if( preview.Show( report ) ) {
    report.Process();
  }
}

int M_Code_App::BatchJob() {
  if( !m_Batch ) {
    return -1;
  }
  cbCompilerPlugin *compiler = Manager::Get()->GetPluginManager()->GetFirstCompiler();
  if( !compiler ) {
    return -3;
  }
  if( !m_Clean && m_BatchTarget.Lower() == _T( "ask" ) ) {
    m_BatchTarget.Clear();
    M_Code_Project* prj = Manager::Get()->GetProjectManager()->GetActiveProject();
    if( prj ) {
      int idx = -1;
      wxString defTarget = prj->GetActiveBuildTarget();
      for( int i = 0; i < prj->GetBuildTargetsCount(); ++i ) {
        ProjectBuildTarget* target = prj->GetBuildTarget( i );
        if( target->GetTitle().Matches( defTarget ) ) {
          idx = i;
          break;
        }
      }
      idx = prj->SelectTarget( idx, false );
      if( idx == -1 ) {
        return 0;
      }
      m_BatchTarget = prj->GetBuildTarget( idx )->GetTitle();
    }
  }
  m_pBatchBuildDialog = m_Frame->GetBatchBuildDialog();
  PlaceWindow( m_pBatchBuildDialog );
  wxString title = _T( "Building '" ) + wxFileNameFromPath( wxString( argv[argc - 1] ) ) + _T( "' (target '" )  + m_BatchTarget + _T( "')" );
  wxTaskBarIcon* tbIcon = new wxTaskBarIcon();
  wxString bb_title = m_pBatchBuildDialog->GetTitle();
  m_pBatchBuildDialog->SetTitle( bb_title + _T( " - " ) + title );
  m_pBatchBuildDialog->Show();
  if( m_ReBuild ) {
    if( m_HasProject ) {
      compiler->Rebuild( m_BatchTarget );
    } else if( m_HasWorkSpace ) {
      compiler->RebuildWorkspace( m_BatchTarget );
    }
  } else if( m_Build ) {
    if( m_HasProject ) {
      compiler->Build( m_BatchTarget );
    } else if( m_HasWorkSpace ) {
      compiler->BuildWorkspace( m_BatchTarget );
    }
  } else if( m_Clean ) {
    if( m_HasProject ) {
      compiler->Clean( m_BatchTarget );
    } else if( m_HasWorkSpace ) {
      compiler->CleanWorkspace( m_BatchTarget );
    }
  }
  if( m_pBatchBuildDialog ) {
    if( !m_Clean ) {
      m_pBatchBuildDialog->ShowModal();
    }
    if( m_pBatchBuildDialog->IsModal() ) {
      m_pBatchBuildDialog->EndModal( wxID_OK );
    } else {
      m_pBatchBuildDialog->Destroy();
      m_pBatchBuildDialog = nullptr;
    }
  }
  if( tbIcon ) {
    tbIcon->RemoveIcon();
    delete tbIcon;
  }
  return 0;
}

void M_Code_App::OnBatchBuildDone( CodeBlocksEvent& event ) {
  event.Skip();
  static bool one_time_only = false;
  if( !m_Batch || one_time_only ) {
    return;
  }
  one_time_only = true;
  cbCompilerPlugin* compiler = static_cast<cbCompilerPlugin*>( event.GetPlugin() );
  m_BatchExitCode = compiler->GetExitCode();
  if( m_BatchNotify ) {
    wxString msg;
    if( m_BatchExitCode == 0 ) {
      msg << _T( "批处理结束.\n" );
    } else {
      msg << _T( "批处理停止错误.\n" );
    }
    msg << F( _T( "状态代码( %d )进程已退出" ), m_BatchExitCode );
    InfoWindow::Tip( msg, _T( "提示" ), 2500 );
  } else {
    wxBell();
  }
  if( m_pBatchBuildDialog && m_BatchWindowAutoClose ) {
    if( m_pBatchBuildDialog->IsModal() ) {
      m_pBatchBuildDialog->EndModal( wxID_OK );
    } else {
      m_pBatchBuildDialog->Destroy();
      m_pBatchBuildDialog = nullptr;
    }
  }
}

void M_Code_App::OnTBIconLeftDown( wxTaskBarIconEvent& event ) {
  event.Skip();
  if( m_pBatchBuildDialog ) {
    m_pBatchBuildDialog->Raise();
    m_pBatchBuildDialog->Refresh();
  }
}

wxString M_Code_App::GetAppPath() const {
  wxString base;
  if( !m_Prefix.IsEmpty() ) {
    return m_Prefix;
  }
  base = wxString( SELFPATH, wxConvUTF8 );
  base = wxFileName( base ).GetPath();
  if( base.IsEmpty() ) {
    base = _T( "." );
  }
  return base;
}

void M_Code_App::SetAutoFile( wxString& file ) {
  m_AutoFile = file;
}

int M_Code_App::ParseCmdLine( MainFrame* handlerFrame, const wxString& CmdLineString ) {
  bool filesInCmdLine = false;
  wxCmdLineParser& parser = *Manager::GetCmdLineParser();
  if( CmdLineString.IsEmpty() ) {
    parser.SetCmdLine( argc, argv );
  } else {
    parser.SetCmdLine( CmdLineString );
  }
  int res = parser.Parse( false );
  if( res == -1 ) {
    return -1;
  } else {
    if( handlerFrame ) {
      m_HasProject = false;
      m_HasWorkSpace = false;
      int count = parser.GetParamCount();
      parser.Found( _T( "file" ), &m_AutoFile );
      filesInCmdLine = ( count != 0 ) || ( !m_AutoFile.empty() );
      for( int param = 0; param < count; ++param ) {
        FileType ft = FileTypeOf( parser.GetParam( param ) );
        wxFileName fn( parser.GetParam( param ) );
        fn.Normalize();
        if( ft == ftm_Code_Project ) {
          m_HasProject = true;
          m_DelayedFilesToOpen.Add( parser.GetParam( param ) );
        } else if( ft == ftm_Code_Workspace ) {
          m_HasWorkSpace = true;
          m_DelayedFilesToOpen.Clear();
          m_DelayedFilesToOpen.Add( fn.GetFullPath() );
          break;
        } else if( wxFile::Exists( fn.GetFullPath() ) ) {
          m_DelayedFilesToOpen.Add( fn.GetFullPath() );
        }
      }
      m_Batch = m_HasProject || m_HasWorkSpace;
      m_Batch = m_Batch && ( m_Build || m_ReBuild || m_Clean );
    } else {
      wxString val;
      parser.Found( _T( "prefix" ), &m_Prefix );
      parser.Found( _T( "user-data-dir" ), &m_UserDataDir );
      m_SafeMode = parser.Found( _T( "safe-mode" ) );
      m_HasDebugLog = parser.Found( _T( "debug-log" ) );
      m_CrashHandler = !parser.Found( _T( "no-crash-handler" ) );
      wxLog::EnableLogging( parser.Found( _T( "verbose" ) ) );
      if( parser.Found( _T( "personality" ), &val ) || parser.Found( _T( "profile" ), &val ) ) {
        SetupPersonality( val );
      }
      m_BatchNotify = parser.Found( _T( "batch-build-notify" ) );
      m_BatchWindowAutoClose = !parser.Found( _T( "no-batch-window-close" ) );
      m_Build = parser.Found( _T( "build" ) );
      m_ReBuild = parser.Found( _T( "rebuild" ) );
      m_Clean = parser.Found( _T( "clean" ) );
      parser.Found( _T( "target" ), &m_BatchTarget );
      parser.Found( _T( "script" ), &m_Script );
      m_Batch = m_Build || m_ReBuild || m_Clean;
      if( parser.Found( _T( "no-log" ) ) == false ) {
        Manager::Get()->GetLogManager()->SetLog( new TextCtrlLogger, LogManager::app_log );
      }
      if( parser.Found( _T( "log-to-file" ) ) ) {
        Manager::Get()->GetLogManager()->SetLog( new FileLogger( _T( "m.log" ) ), LogManager::app_log );
      }
      if( m_HasDebugLog ) {
        Manager::Get()->GetLogManager()->SetLog( new TextCtrlLogger, LogManager::debug_log );
      }
      if( parser.Found( _T( "debug-log-to-file" ) ) ) {
        Manager::Get()->GetLogManager()->SetLog( new FileLogger( _T( "m-debug.log" ) ), LogManager::debug_log );
      }
    }
  }
  return filesInCmdLine ? 1 : 0;
}

void M_Code_App::SetupPersonality( const wxString& personality ) {
  if( personality.CmpNoCase( _T( "ask" ) ) == 0 ) {
    const wxArrayString items( Manager::Get()->GetPersonalityManager()->GetPersonalitiesList() );
    wxSingleChoiceDialog dlg( nullptr, _T( "选择要加载的配置文件:" ), _T( "自定义配置" ), items );
    if( dlg.ShowModal() == wxID_OK ) {
      Manager::Get()->GetPersonalityManager()->SetPersonality( dlg.GetStringSelection() );
    }
  } else {
    Manager::Get()->GetPersonalityManager()->SetPersonality( personality, true );
  }
}

void M_Code_App::LoadDelayedFiles( MainFrame *const frame ) {
  std::set<wxString> uniqueFilesToOpen( m_DelayedFilesToOpen.begin(), m_DelayedFilesToOpen.end() );
  for( std::set<wxString>::const_iterator it = uniqueFilesToOpen.begin(); it != uniqueFilesToOpen.end(); ++it ) {  // 只读迭代
    frame->Open( *it, true );
  }
  m_DelayedFilesToOpen.Clear();
  if( !m_AutoFile.IsEmpty() ) {
    wxString linePart;
    wxString filePart;
    long linePos = m_AutoFile.Find( _T( ':' ), true );
    if( linePos != wxNOT_FOUND ) {
      linePart = m_AutoFile.Mid( linePos + 1, wxString::npos );
      filePart = m_AutoFile;
      filePart.Remove( linePos );
    }
    long line = -1;
    if( linePos != wxNOT_FOUND ) {
      if( !linePart.ToLong( &line ) ) {
        filePart = m_AutoFile;
      }
    }
    wxFileName fn( filePart );
    fn.Normalize();
    if( frame->Open( fn.GetFullPath(), false ) ) {
      EditorBase* eb = Manager::Get()->GetEditorManager()->GetEditor( fn.GetFullPath() );
      if( eb && ( line != -1 ) ) {
        eb->GotoLine( line - 1, true );
      }
    }
    m_AutoFile.Clear();
  }
}

void M_Code_App::OnAppActivate( wxActivateEvent& event ) {
  event.Skip();
  if( s_Loading ) {
    return;
  }
  Manager *manager = Manager::Get();
  if( !manager || manager->IsAppShuttingDown() ) {
    return;
  }
  CodeBlocksEvent Event;
  if( event.GetActive() ) {
    Event.SetEventType( cbEVT_APP_ACTIVATED );
  } else {
    Event.SetEventType( cbEVT_APP_DEACTIVATED );
  }
  Manager::Get()->ProcessEvent( Event );
  if( !event.GetActive() ) {
    return;
  }
  if( !m_Batch && Manager::Get()->GetEditorManager() && Manager::Get()->GetConfigManager( _T( "app" ) )->ReadBool( _T( "/environment/check_modified_files" ), true ) ) {
    wxCommandEvent evt( wxEVT_COMMAND_MENU_SELECTED, idEditorManagerCheckFiles );
    wxPostEvent( Manager::Get()->GetEditorManager(), evt );
    cbProjectManagerUI *prjManUI = m_Frame->GetProjectManagerUI();
    if( prjManUI ) {
      static_cast<ProjectManagerUI*>( prjManUI )->CheckForExternallyModifiedProjects();
    }
  }
  M_Code_Editor* ed = Manager::Get()->GetEditorManager() ? Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor() : nullptr;
  if( ed ) {
    Manager::Get()->GetEditorManager()->GetNotebook()->SetFocus();
    ed->GetControl()->SetFocus();
  }
}

void M_Code_App::AddFileToOpenDelayed( const wxString& filename ) {
  m_DelayedFilesToOpen.Add( filename );
}
